//
//  CalendarCollectionViewLayout.m
//  OfficeBook
//
//  Created by 孙晓奇 on 15/9/25.
//  Copyright © 2015年 sunny. All rights reserved.
//


#import "CalendarCollectionViewLayout.h"
#import "CalendarEventCell.h"

NSString * const MSCollectionElementKindTimeRowHeader = @"MSCollectionElementKindTimeRow";
NSString * const MSCollectionElementKindDayColumnHeader = @"MSCollectionElementKindDayHeader";
NSString * const MSCollectionElementKindTimeRowHeaderBackground = @"MSCollectionElementKindTimeRowHeaderBackground";
NSString * const MSCollectionElementKindDayColumnHeaderBackground = @"MSCollectionElementKindDayColumnHeaderBackground";
NSString * const MSCollectionElementKindCurrentTimeIndicator = @"MSCollectionElementKindCurrentTimeIndicator";
NSString * const MSCollectionElementKindCurrentTimeVerticalGridline = @"MSCollectionElementKindCurrentTimeVerticalGridline";

NSString * const MSCollectionElementKindVerticalDashGridline = @"MSCollectionElementKindVerticalDashGridline";
NSString * const MSCollectionElementKindVerticalGridline = @"MSCollectionElementKindVerticalGridline";

NSUInteger const MSCollectionMinOverlayZ = 1000.0; // Allows for 900 items in a section without z overlap issues
NSUInteger const MSCollectionMinCellZ = 100.0;  // Allows for 100 items in a section's background
NSUInteger const MSCollectionMinBackgroundZ = 0.0;


@interface MSTimerWeakTarget : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
- (SEL)fireSelector;
@end

@implementation MSTimerWeakTarget
- (id)initWithTarget:(id)target selector:(SEL)selector
{
    self = [super init];
    if (self) {
        self.target = target;
        self.selector = selector;
    }
    return self;
}
- (void)fire:(NSTimer*)timer
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [self.target performSelector:self.selector withObject:timer];
#pragma clang diagnostic pop
}
- (SEL)fireSelector
{
    return @selector(fire:);
}
@end

@interface CalendarCollectionViewLayout ()

// Minute Timer
@property (nonatomic, strong) NSTimer *minuteTimer;

// Caches
@property (nonatomic, assign) BOOL needsToPopulateAttributesForAllSections;
@property (nonatomic, strong) NSCache *cachedDayDateComponents;
@property (nonatomic, strong) NSCache *cachedStartTimeDateComponents;
@property (nonatomic, strong) NSCache *cachedEndTimeDateComponents;
@property (nonatomic, strong) NSCache *cachedCurrentDateComponents;
@property (nonatomic, assign) CGFloat cachedMaxColumnHeight;
@property (nonatomic, assign) NSInteger cachedEarliestHour;
@property (nonatomic, assign) NSInteger cachedLatestHour;
@property (nonatomic, strong) NSMutableDictionary *cachedColumnWidths;
@property (nonatomic, strong) NSMutableDictionary *cachedEarliestHours;
@property (nonatomic, strong) NSMutableDictionary *cachedLatestHours;

// Registered Decoration Classes
@property (nonatomic, strong) NSMutableDictionary *registeredDecorationClasses;

// Attributes
@property (nonatomic, strong) NSMutableArray *allAttributes;
@property (nonatomic, strong) NSMutableDictionary *itemAttributes;
@property (nonatomic, strong) NSMutableDictionary *dayColumnHeaderAttributes;
@property (nonatomic, strong) NSMutableDictionary *dayColumnHeaderBackgroundAttributes;
@property (nonatomic, strong) NSMutableDictionary *timeRowHeaderAttributes;
@property (nonatomic, strong) NSMutableDictionary *timeRowHeaderBackgroundAttributes;
@property (nonatomic, strong) NSMutableDictionary *verticalGridlineAttributes;
@property (nonatomic, strong) NSMutableDictionary *verticalDashGridlineAttributes;
@property (nonatomic, strong) NSMutableDictionary *currentTimeIndicatorAttributes;
@property (nonatomic, strong) NSMutableDictionary *currentTimeVerticalGridlineAttributes;

@end

@implementation CalendarCollectionViewLayout

#pragma mark - NSObject

- (void)dealloc
{
}

- (id)init
{
    self = [super init];
    if (self) {
        self.needsToPopulateAttributesForAllSections = YES;
        self.cachedDayDateComponents = [NSCache new];
        self.cachedStartTimeDateComponents = [NSCache new];
        self.cachedEndTimeDateComponents = [NSCache new];
        self.cachedCurrentDateComponents = [NSCache new];
        self.cachedMaxColumnHeight = CGFLOAT_MIN;
        self.cachedEarliestHour = NSIntegerMax;
        self.cachedLatestHour = NSIntegerMin;
        self.cachedColumnWidths = [NSMutableDictionary new];
        self.cachedEarliestHours = [NSMutableDictionary new];
        self.cachedLatestHours = [NSMutableDictionary new];
        
        self.registeredDecorationClasses = [NSMutableDictionary new];
        
        self.allAttributes = [NSMutableArray new];
        self.itemAttributes = [NSMutableDictionary new];
        self.dayColumnHeaderAttributes = [NSMutableDictionary new];
        self.dayColumnHeaderBackgroundAttributes = [NSMutableDictionary new];
        self.timeRowHeaderAttributes = [NSMutableDictionary new];
        self.timeRowHeaderBackgroundAttributes = [NSMutableDictionary new];
        self.verticalGridlineAttributes = [NSMutableDictionary new];
        self.verticalDashGridlineAttributes = [NSMutableDictionary new];
        self.currentTimeIndicatorAttributes = [NSMutableDictionary new];
        self.currentTimeVerticalGridlineAttributes = [NSMutableDictionary new];
        
        self.dayColumnHeaderWidth = 20.0;
        self.hourWidth = 60.0f;
        self.minuteWidth = self.hourWidth / 60.0;
        self.timeRowHeaderHeight = 25.0;
        
        self.currentTimeIndicatorSize = CGSizeMake(self.hourWidth, self.timeRowHeaderHeight);
        self.currentTimeVerticalGridlineWidth = 10.0;
        self.verticalGridlineWidth = 1.0;
        
        self.contentMargin = UIEdgeInsetsMake(5.0, 15.0, 0.0, 15.0);
        
        self.displayHeaderBackgroundAtOrigin = YES;  //header在屏幕最左边面 部分内容离开屏幕,但没有完全离开
        self.headerLayoutType = MSHeaderLayoutTypeDayColumnAboveTimeRow;
        
        // Invalidate layout on minute ticks (to update the position of the current time indicator)
        NSDate *oneMinuteInFuture = [[NSDate date] dateByAddingTimeInterval:60];  // 精确到秒
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:oneMinuteInFuture];
        NSDate *nextMinuteBoundary = [[NSCalendar currentCalendar] dateFromComponents:components]; //去除秒的部分
        
        // This needs to be a weak reference, otherwise we get a retain cycle
        MSTimerWeakTarget *timerWeakTarget = [[MSTimerWeakTarget alloc] initWithTarget:self selector:@selector(minuteTick:)];
        self.minuteTimer = [[NSTimer alloc] initWithFireDate:nextMinuteBoundary interval:60 target:timerWeakTarget selector:timerWeakTarget.fireSelector userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.minuteTimer forMode:NSDefaultRunLoopMode];
        
    }
    return self;
}


#pragma mark Minute Updates
- (void)minuteTick:(id)sender
{
    // Invalidate cached current date componets (since the minute's changed!)
    [self.cachedCurrentDateComponents removeAllObjects];
    [self invalidateLayout];
}


#pragma mark - UICollectionViewLayout

- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
    [self invalidateLayoutCache];
    
    // Update the layout with the new items
    [self prepareLayout];
    
    [super prepareForCollectionViewUpdates:updateItems];
}

- (void)finalizeCollectionViewUpdates
{
    // This is a hack to prevent the error detailed in :
    // http://stackoverflow.com/questions/12857301/uicollectionview-decoration-and-supplementary-views-can-not-be-moved
    // If this doesn't happen, whenever the collection view has batch updates performed on it, we get multiple instantiations of decoration classes
    for (UIView *subview in self.collectionView.subviews) {
        for (Class decorationViewClass in self.registeredDecorationClasses.allValues) {
            if ([subview isKindOfClass:decorationViewClass]) {
                [subview removeFromSuperview];
            }
        }
    }
    [self.collectionView reloadData];
}


//重写该方法
- (void)registerClass:(Class)viewClass forDecorationViewOfKind:(NSString *)decorationViewKind
{
    [super registerClass:viewClass forDecorationViewOfKind:decorationViewKind];
    self.registeredDecorationClasses[decorationViewKind] = viewClass;
}

- (void)prepareLayout
{
    [super prepareLayout];
    
    if (self.needsToPopulateAttributesForAllSections) {
        [self prepareSectionLayoutForSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];
        self.needsToPopulateAttributesForAllSections = NO;
    }
    
    BOOL needsToPopulateAllAttribtues = (self.allAttributes.count == 0);
    if (needsToPopulateAllAttribtues) {
        [self.allAttributes addObjectsFromArray:[self.dayColumnHeaderAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.dayColumnHeaderBackgroundAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.timeRowHeaderAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.timeRowHeaderBackgroundAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.verticalGridlineAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.verticalDashGridlineAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.itemAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.currentTimeIndicatorAttributes allValues]];
        [self.allAttributes addObjectsFromArray:[self.currentTimeVerticalGridlineAttributes allValues]];
    }
}

//主要布局函数
- (void)prepareSectionLayoutForSections:(NSIndexSet *)sectionIndexes
{
    if (self.collectionView.numberOfSections == 0) {
        return;
    }
    
    // Current Time Indicator
    NSIndexPath *currentTimeIndicatorIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    UICollectionViewLayoutAttributes *currentTimeIndicatorAttributes = [self layoutAttributesForDecorationViewAtIndexPath:currentTimeIndicatorIndexPath ofKind:MSCollectionElementKindCurrentTimeIndicator withItemCache:self.currentTimeIndicatorAttributes];
    
    // Current Time Vertical Gridline
    NSIndexPath *currentTimeVerticalGridlineIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    UICollectionViewLayoutAttributes *currentTimeVerticalGridlineAttributes = [self layoutAttributesForDecorationViewAtIndexPath:currentTimeVerticalGridlineIndexPath ofKind:MSCollectionElementKindCurrentTimeVerticalGridline withItemCache:self.currentTimeVerticalGridlineAttributes];
    
    // Start these off hidden, and unhide them in the case of the current time indicator being within a specified section
    currentTimeIndicatorAttributes.frame = CGRectZero;
    currentTimeVerticalGridlineAttributes.frame = CGRectZero;
    
    
    BOOL needsToPopulateItemAttributes = (self.itemAttributes.count == 0);
    BOOL needsToPopulateVerticalGridlineAttributes = ((self.verticalGridlineAttributes.count == 0) || ((self.verticalDashGridlineAttributes.count == 0)));
    
    self.sectionHeight = (self.collectionViewContentSize.height - self.timeRowHeaderHeight - self.contentMargin.bottom - self.contentMargin.top);
    //CGFloat verticalGridMinX = (self.dayColumnHeaderWidth + self.contentMargin.left);
    CGFloat verticalGridHeight = self.sectionHeight;
    
    [sectionIndexes enumerateIndexesUsingBlock:^(NSUInteger section, BOOL *stop) {
        
        CGFloat columnMinX = (section == 0) ? 0.0 : [self stackedSectionWidthUpToSection:section];  //段偏移
        CGFloat nextColumnMinX = (section == self.collectionView.numberOfSections) ? self.collectionViewContentSize.width : [self stackedSectionWidthUpToSection:(section + 1)];
        CGFloat verticalGridMinX = (columnMinX + self.dayColumnHeaderWidth + self.contentMargin.left);   //每段第一根GridLine X
        
        //-- Day Column Header --//
        CGFloat dayColumnHeaderMinX = fminf(fmaxf(self.collectionView.contentOffset.x, columnMinX), (nextColumnMinX - self.dayColumnHeaderWidth));
        BOOL dayColumnHeaderFloating = ((dayColumnHeaderMinX > columnMinX) || self.displayHeaderBackgroundAtOrigin);
        NSIndexPath *dayColumnHeaderIndexPath = [NSIndexPath indexPathForRow:0 inSection:section];
        UICollectionViewLayoutAttributes *dayColumnHeaderAttributes = [self layoutAttributesForSupplementaryViewAtIndexPath:dayColumnHeaderIndexPath ofKind:MSCollectionElementKindDayColumnHeader withItemCache:self.dayColumnHeaderAttributes];
        // Frame
        dayColumnHeaderAttributes.frame = CGRectMake(dayColumnHeaderMinX, 0.0, self.dayColumnHeaderWidth, self.collectionViewContentSize.height);
        dayColumnHeaderAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindDayColumnHeader floating:dayColumnHeaderFloating];
  
        //--!! For Items !!--//
        if (needsToPopulateItemAttributes) {
            // Items
            NSMutableArray *sectionItemAttributes = [NSMutableArray new];
            for (NSInteger item = 0; item < [self.collectionView numberOfItemsInSection:section]; item++) {
                NSIndexPath *itemIndexPath = [NSIndexPath indexPathForItem:item inSection:section];
                UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForCellAtIndexPath:itemIndexPath withItemCache:self.itemAttributes];
                [sectionItemAttributes addObject:itemAttributes];
                
                NSDateComponents *itemStartTime = [self startTimeForIndexPath:itemIndexPath];
                NSDateComponents *itemEndTime = [self endTimeForIndexPath:itemIndexPath];
                
                CGFloat startHourX = (itemStartTime.hour * self.hourWidth);
                CGFloat startMinuteX = (itemStartTime.minute * self.minuteWidth);
                
                CGFloat endHourX;
                if (itemEndTime.day != itemStartTime.day) {
                    
                    endHourX = ([[NSCalendar currentCalendar] maximumRangeOfUnit:NSCalendarUnitHour].length * self.hourWidth) + (itemEndTime.hour * self.hourWidth);
                } else {
                    endHourX = (itemEndTime.hour * self.hourWidth);
                }
                CGFloat endMinuteX = (itemEndTime.minute * self.minuteWidth);
                
                CGFloat itemMinX = nearbyintf(startHourX + startMinuteX + verticalGridMinX - self.cellMargin.left);
                CGFloat itemMaxX = nearbyintf(endHourX + endMinuteX + verticalGridMinX + self.cellMargin.right);
                
                self.itemHeight = 0.70 * verticalGridHeight;
                CGFloat itemMinY = nearbyintf(self.contentMargin.top + (verticalGridHeight - self.itemHeight) / 2.f);
                itemAttributes.frame = CGRectMake(itemMinX, itemMinY, (itemMaxX - itemMinX), self.itemHeight);
                
                itemAttributes.zIndex = [self zIndexForElementKind:nil];
            }

            [self adjustItemsForOverlap:sectionItemAttributes inSection:section sectionMinY:self.contentMargin.top];
        }

        //-- Vertical Gridlines and DashGridlines --//
        if (needsToPopulateVerticalGridlineAttributes) {
            NSUInteger verticalGridlineIndex = 0;
            for (NSInteger hour = 0; hour <= 24; hour++) {
                NSIndexPath *verticalGridlineIndexPath = [NSIndexPath indexPathForItem:verticalGridlineIndex inSection:section];
                UICollectionViewLayoutAttributes *verticalGridlineAttributes = [self layoutAttributesForDecorationViewAtIndexPath:verticalGridlineIndexPath ofKind:MSCollectionElementKindVerticalGridline withItemCache:self.verticalGridlineAttributes];
                // Frame
                CGFloat verticalGridlineX = (verticalGridMinX + (self.hourWidth * hour)) - nearbyintf(self.verticalGridlineWidth / 2.0);
                CGFloat verticalGridlineY = self.contentMargin.top;
                verticalGridlineAttributes.frame = CGRectMake(verticalGridlineX, verticalGridlineY, self.verticalGridlineWidth, verticalGridHeight);
                verticalGridlineAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindVerticalGridline];
                
                //- DashLines -//
                NSIndexPath *verticalDashGridlineIndexPath = [NSIndexPath indexPathForItem:verticalGridlineIndex inSection:section];
                UICollectionViewLayoutAttributes *verticalDashGridlineAttributes = [self layoutAttributesForDecorationViewAtIndexPath:verticalDashGridlineIndexPath ofKind:MSCollectionElementKindVerticalDashGridline withItemCache:self.verticalDashGridlineAttributes];
                // Frame
                CGFloat verticalDashGridlineX = (verticalGridMinX + (self.hourWidth * (hour + 0.5))) - nearbyintf(self.verticalGridlineWidth / 2.0);
                CGFloat verticalDashGridlineY = self.contentMargin.top;
                verticalDashGridlineAttributes.frame = CGRectMake(verticalDashGridlineX, verticalDashGridlineY, self.verticalGridlineWidth, verticalGridHeight);
                verticalDashGridlineAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindVerticalDashGridline];
                
                verticalGridlineIndex++;
            }
        }
        
        //-- Time Row Headers --//
        CGFloat TimeRowHeaderY = verticalGridHeight + self.contentMargin.top + self.contentMargin.bottom;
        NSUInteger timeRowHeaderIndex = 0;
        for (NSInteger hour = 0; hour <= 24; hour++) {
            // Time Row Header
            NSIndexPath *timeRowHeaderIndexPath = [NSIndexPath indexPathForItem:timeRowHeaderIndex inSection:section];
            UICollectionViewLayoutAttributes *timeRowHeaderAttributes = [self layoutAttributesForSupplementaryViewAtIndexPath:timeRowHeaderIndexPath ofKind:MSCollectionElementKindTimeRowHeader withItemCache:self.timeRowHeaderAttributes];
            // Frame
            CGFloat titleRowHeaderX = (verticalGridMinX + (self.hourWidth * hour)) - nearbyintf(self.hourWidth / 2.0);
            timeRowHeaderAttributes.frame = CGRectMake(titleRowHeaderX, TimeRowHeaderY, self.hourWidth, self.timeRowHeaderHeight);
            timeRowHeaderAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindTimeRowHeader];
            timeRowHeaderIndex++;
        }
        
        //-- Current Time Gridline and Indicator --//
        NSDateComponents *sectionDay = [self.delegate dayForSection:section];
        NSDateComponents *currentTimeDateComponents = [self currentTimeDateComponents];
        // The current time is within this section's day
        if (currentTimeDateComponents.day == sectionDay.day) {
            // The x value of the current time
            CGFloat timeX = (verticalGridMinX + nearbyintf((currentTimeDateComponents.hour * self.hourWidth) + (currentTimeDateComponents.minute * self.minuteWidth)));
            
            CGFloat currentTimeIndicatorX = (timeX - nearbyintf(self.currentTimeIndicatorSize.width / 2.0));
            CGFloat currentTimeIndicatorY = TimeRowHeaderY;
            currentTimeIndicatorAttributes.frame = (CGRect){{currentTimeIndicatorX, currentTimeIndicatorY}, self.currentTimeIndicatorSize};
            currentTimeIndicatorAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindCurrentTimeIndicator];
            
            CGFloat currentTimeVerticalGridlineX = (timeX - nearbyintf(self.currentTimeVerticalGridlineWidth / 2.0));
            currentTimeVerticalGridlineAttributes.frame = CGRectMake(currentTimeVerticalGridlineX, 0, self.currentTimeVerticalGridlineWidth, verticalGridHeight + 2 * self.contentMargin.top);
            currentTimeVerticalGridlineAttributes.zIndex = [self zIndexForElementKind:MSCollectionElementKindCurrentTimeVerticalGridline];
        }
    }];
}

// 防止段内多个事件相互覆盖
- (void)adjustItemsForOverlap:(NSArray *)sectionItemAttributes inSection:(NSUInteger)section sectionMinY:(CGFloat)sectionMinY
{
    NSMutableSet *adjustedAttributes = [NSMutableSet new];
    NSUInteger sectionZ = MSCollectionMinCellZ;
    
    for (UICollectionViewLayoutAttributes *itemAttributes in sectionItemAttributes) {
        
        // If an item's already been adjusted, move on to the next one
        if ([adjustedAttributes containsObject:itemAttributes]) {
            continue;
        }
        
        // Find the other items that overlap with this item
        NSMutableArray *overlappingItems = [NSMutableArray new];
        // 实际蓝色body部分
        CGRect itemFrame = CGRectMake(CGRectGetMinX(itemAttributes.frame) - CalendarCellPadding, CGRectGetMinY(itemAttributes.frame),
                                      CGRectGetWidth(itemAttributes.frame) - 2 * CalendarCellPadding, CGRectGetHeight(itemAttributes.frame));
        [overlappingItems addObjectsFromArray:[sectionItemAttributes filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *layoutAttributes, NSDictionary *bindings) {
            if ((layoutAttributes != itemAttributes)) {
                CGRect layoutFrame = CGRectMake(CGRectGetMinX(layoutAttributes.frame) - CalendarCellPadding, CGRectGetMinY(layoutAttributes.frame),
                                            CGRectGetWidth(layoutAttributes.frame) - 2 * CalendarCellPadding, CGRectGetHeight(layoutAttributes.frame));
                return CGRectIntersectsRect(itemFrame, layoutFrame);
            } else {
                return NO;
            }
        }]]];
        
        // If there's items overlapping, we need to adjust them
        if (overlappingItems.count) {
            
            // Add the item we're adjusting to the overlap set
            [overlappingItems insertObject:itemAttributes atIndex:0];
            
            // Find the minX and maxX of the set
            CGFloat minX = CGFLOAT_MAX;
            CGFloat maxX = CGFLOAT_MIN;
            for (UICollectionViewLayoutAttributes *overlappingItemAttributes in overlappingItems) {
                if (CGRectGetMinX(overlappingItemAttributes.frame) < minX) {
                    minX = CGRectGetMinX(overlappingItemAttributes.frame);
                }
                if (CGRectGetMaxX(overlappingItemAttributes.frame) > maxX) {
                    maxX = CGRectGetMaxX(overlappingItemAttributes.frame);
                }
            }
            
            // Determine the number of divisions needed (maximum number of currently overlapping items)
            NSInteger divisions = 1;
            for (CGFloat currentX = minX; currentX <= maxX; currentX += 1.0) {
                NSInteger numberItemsForCurrentX = 0;
                for (UICollectionViewLayoutAttributes *overlappingItemAttributes in overlappingItems) {
                    if ((currentX >= CGRectGetMinX(overlappingItemAttributes.frame)) && (currentX < CGRectGetMaxX(overlappingItemAttributes.frame))) {
                        numberItemsForCurrentX++;
                    }
                }
                if (numberItemsForCurrentX > divisions) {
                    divisions = numberItemsForCurrentX;
                }
            }
            
            // Adjust the items to have a height of the section size divided by the number of divisions needed
            CGFloat divisionHeight = nearbyintf(self.sectionHeight / divisions);
            
            NSMutableArray *dividedAttributes = [NSMutableArray array];
            for (UICollectionViewLayoutAttributes *divisionAttributes in overlappingItems) {
                
                CGFloat itemHeight = (divisionHeight - self.cellMargin.top - self.cellMargin.bottom);
                
                // It it hasn't yet been adjusted, perform adjustment
                if (![adjustedAttributes containsObject:divisionAttributes]) {
                    
                    CGRect divisionAttributesFrame = divisionAttributes.frame;
                    divisionAttributesFrame.origin.y = (sectionMinY + self.cellMargin.top);
                    divisionAttributesFrame.size.height = itemHeight;
                    
                    // vertical Layout
                    NSInteger adjustments = 1;
                    for (UICollectionViewLayoutAttributes *dividedItemAttributes in dividedAttributes) {
                        if (CGRectIntersectsRect(dividedItemAttributes.frame, divisionAttributesFrame)) {
                            divisionAttributesFrame.origin.y = sectionMinY + ((divisionHeight * adjustments) + self.cellMargin.top);
                            adjustments++;
                        }
                    }
                    
                    // Stacking (lower items stack above higher items, since the title is at the top)
                    divisionAttributes.zIndex = sectionZ;
                    sectionZ ++;
                    
                    divisionAttributes.frame = divisionAttributesFrame;
                    [dividedAttributes addObject:divisionAttributes];
                    [adjustedAttributes addObject:divisionAttributes];
                }
            }
        }
    }
}


- (void)invalidateLayoutCache
{
    self.needsToPopulateAttributesForAllSections = YES;
    
    // Invalidate cached Components
    [self.cachedDayDateComponents removeAllObjects];
    [self.cachedStartTimeDateComponents removeAllObjects];
    [self.cachedEndTimeDateComponents removeAllObjects];
    [self.cachedCurrentDateComponents removeAllObjects];
    
    // Invalidate cached interface sizing values
    self.cachedEarliestHour = NSIntegerMax;
    self.cachedLatestHour = NSIntegerMin;
    self.cachedMaxColumnHeight = CGFLOAT_MIN;
    [self.cachedColumnWidths removeAllObjects];
    [self.cachedEarliestHours removeAllObjects];
    [self.cachedLatestHours removeAllObjects];
    
    // Invalidate cached item attributes
    [self.itemAttributes removeAllObjects];
    [self.verticalGridlineAttributes removeAllObjects];
    [self.verticalDashGridlineAttributes removeAllObjects];
    [self.dayColumnHeaderAttributes removeAllObjects];
    [self.dayColumnHeaderBackgroundAttributes removeAllObjects];
    [self.timeRowHeaderAttributes removeAllObjects];
    [self.timeRowHeaderBackgroundAttributes removeAllObjects];
    [self.allAttributes removeAllObjects];
}

#pragma mark - Layout cache
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewAtIndexPath:(NSIndexPath *)indexPath ofKind:(NSString *)kind withItemCache:(NSMutableDictionary *)itemCache
{
    UICollectionViewLayoutAttributes *layoutAttributes;
    if (self.registeredDecorationClasses[kind] && !(layoutAttributes = itemCache[indexPath])) {
        layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath];
        itemCache[indexPath] = layoutAttributes;
    }
    return layoutAttributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewAtIndexPath:(NSIndexPath *)indexPath ofKind:(NSString *)kind withItemCache:(NSMutableDictionary *)itemCache
{
    //NSLog(@"%s: current section: %li", __FUNCTION__, (long)indexPath.section);
    
    UICollectionViewLayoutAttributes *layoutAttributes;
    if (!(layoutAttributes = itemCache[indexPath])) {
        layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:kind withIndexPath:indexPath];
        itemCache[indexPath] = layoutAttributes;
    }
    return layoutAttributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForCellAtIndexPath:(NSIndexPath *)indexPath withItemCache:(NSMutableDictionary *)itemCache
{
    UICollectionViewLayoutAttributes *layoutAttributes;
    if (!(layoutAttributes = itemCache[indexPath])) {
        layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        itemCache[indexPath] = layoutAttributes;
    }
    return layoutAttributes;
}


#pragma mark 以下几个是collectionView flowLayout要求重写的方法
- (CGSize)collectionViewContentSize
{
    CGFloat width = [self stackedSectionWidth];
    CGFloat height = self.collectionViewHeight;
    return CGSizeMake(width, height);
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return self.itemAttributes[indexPath];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    if (kind == MSCollectionElementKindDayColumnHeader) {
        return self.dayColumnHeaderAttributes[indexPath];
    }
    else if (kind == MSCollectionElementKindTimeRowHeader) {
        return self.timeRowHeaderAttributes[indexPath];
    }
    return nil;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath
{
    if (decorationViewKind == MSCollectionElementKindCurrentTimeIndicator) {
        return self.currentTimeIndicatorAttributes[indexPath];
    }
    else if (decorationViewKind == MSCollectionElementKindCurrentTimeVerticalGridline) {
        return self.currentTimeVerticalGridlineAttributes[indexPath];
    }
    else if (decorationViewKind == MSCollectionElementKindVerticalGridline) {
        return self.verticalGridlineAttributes[indexPath];
    }
    else if (decorationViewKind == MSCollectionElementKindVerticalDashGridline){
        return self.verticalDashGridlineAttributes[indexPath];
    }
    else if (decorationViewKind == MSCollectionElementKindTimeRowHeaderBackground) {
        return self.timeRowHeaderBackgroundAttributes[indexPath];
    }
    else if (decorationViewKind == MSCollectionElementKindDayColumnHeader) {
        return self.dayColumnHeaderBackgroundAttributes[indexPath];
    }
    return nil;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableIndexSet *visibleSections = [NSMutableIndexSet indexSet];
    // 计算可视矩形中的sections
    [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)] enumerateIndexesUsingBlock:^(NSUInteger section, BOOL *stop) {
        CGRect sectionRect = [self rectForSection:section];
        if (CGRectIntersectsRect(sectionRect, rect)) {
            [visibleSections addIndex:section];
        }
    }];
    
    // Update layout for only the visible sections
    [self prepareSectionLayoutForSections:visibleSections];
    
    // 可视范围内第一个section所在日期 作为topBar Title时间
    [visibleSections enumerateIndexesUsingBlock:^(NSUInteger section, BOOL * _Nonnull stop) {
        CGFloat columnMinX = (section == 0) ? 0.0 : [self stackedSectionWidthUpToSection:section];  //段偏移
        if (columnMinX <= self.collectionView.contentOffset.x) {
            //NSLog(@"%lu %f %f", (unsigned long)section, columnMinX, self.collectionView.contentOffset.x);
            [self.delegate collectionView:self.collectionView dateTitleAtCurrentSection:section];
        }
    }];
    
    // Return the visible attributes (rect intersection)
    return [self.allAttributes filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UICollectionViewLayoutAttributes *layoutAttributes, NSDictionary *bindings) {
        return CGRectIntersectsRect(rect, layoutAttributes.frame);
    }]];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    // Required for sticky headers
    return YES;
}

#pragma mark Section Sizing
// 返回section的所在矩形
- (CGRect)rectForSection:(NSInteger)section
{
    CGRect sectionRect;
    
    CGFloat columnMinX = (section == 0) ? 0.0 : [self stackedSectionWidthUpToSection:section];
    CGFloat nextColumnMinX = (section == self.collectionView.numberOfSections) ? self.collectionViewContentSize.width : [self stackedSectionWidthUpToSection:(section + 1)];
    sectionRect = CGRectMake(columnMinX, 0.0, (nextColumnMinX - columnMinX), self.collectionViewContentSize.width);
    
    return sectionRect;
}

// 所有section总宽度
- (CGFloat)stackedSectionWidth
{
    return [self stackedSectionWidthUpToSection:self.collectionView.numberOfSections];
}

// 返回段实际originX
- (CGFloat)stackedSectionWidthUpToSection:(NSInteger)upToSection
{
    if (self.cachedColumnWidths[@(upToSection)]) {
        return [self.cachedColumnWidths[@(upToSection)] integerValue];
    }
    CGFloat stackedSectionWidth = self.hourWidth * 24 * upToSection;  //每个段的累积宽度
    CGFloat headerAdjustedStackedColumnWidth = (stackedSectionWidth + ((self.dayColumnHeaderWidth + self.contentMargin.left + self.contentMargin.right) * upToSection));
    if (stackedSectionWidth != 0.0) {
        self.cachedColumnWidths[@(upToSection)] = @(headerAdjustedStackedColumnWidth);   //缓存宽度
        return headerAdjustedStackedColumnWidth;
    } else {
        return headerAdjustedStackedColumnWidth;
    }
}

#pragma mark Scroll至当前时间
- (void)scrollCollectionViewToClosetSectionToCurrentTimeAnimated:(BOOL)animated
{
    if (self.collectionView.numberOfSections != 0) {
        NSInteger closestSectionToCurrentTime = 0;
        CGPoint contentOffset;
        CGRect currentTimeVerticalGridlineAttributesFrame = [self.currentTimeVerticalGridlineAttributes[[NSIndexPath indexPathForItem:0 inSection:0]] frame];
        
        CGFloat xOffset;
        if (!CGRectEqualToRect(currentTimeVerticalGridlineAttributesFrame, CGRectZero)) {
            xOffset = fmaxf(nearbyintf(CGRectGetMinX(currentTimeVerticalGridlineAttributesFrame) - (CGRectGetWidth(self.collectionView.frame) / 2.0)), [self stackedSectionWidthUpToSection:closestSectionToCurrentTime]);
        } else {
            xOffset = [self stackedSectionWidthUpToSection:closestSectionToCurrentTime];
        }
        contentOffset = CGPointMake(xOffset, 0.0);
        
        // Prevent the content offset from forcing the scroll view content off its bounds
        if (contentOffset.x > (self.collectionView.contentSize.width - self.collectionView.frame.size.width)) {
            contentOffset.x = (self.collectionView.contentSize.width - self.collectionView.frame.size.width);
        }
        if (contentOffset.x < 0.0) {
            contentOffset.x = 0.0;
        }
        if (contentOffset.y > (self.collectionView.contentSize.height - self.collectionView.frame.size.height)) {
            contentOffset.y = (self.collectionView.contentSize.height - self.collectionView.frame.size.height);
        }
        if (contentOffset.y < 0.0) {
            contentOffset.y = 0.0;
        }
        [self.collectionView setContentOffset:contentOffset animated:animated];
    }
}

#pragma mark Z Index
- (CGFloat)zIndexForElementKind:(NSString *)elementKind
{
    return [self zIndexForElementKind:elementKind floating:NO];
}

- (CGFloat)zIndexForElementKind:(NSString *)elementKind floating:(BOOL)floating
{
    // Day Column Header
    if (elementKind == MSCollectionElementKindDayColumnHeader) {
        return (MSCollectionMinOverlayZ + (floating ? 6.0 : 4.0));
    }
    // Day Column Header Background
    else if (elementKind == MSCollectionElementKindDayColumnHeaderBackground) {
        return (MSCollectionMinOverlayZ + (floating ? 5.0 : 3.0));
    }
    // Current Time Indicator
    else if (elementKind == MSCollectionElementKindCurrentTimeIndicator) {
        return (MSCollectionMinOverlayZ + 2.0);
    }
    // Time Row Header
    if (elementKind == MSCollectionElementKindTimeRowHeader) {
        return (MSCollectionMinOverlayZ + 1.0);
    }
    // Time Row Header Background
    else if (elementKind == MSCollectionElementKindTimeRowHeaderBackground) {
        return MSCollectionMinOverlayZ;
    }
    // Cell
    else if (elementKind == nil) {
        return MSCollectionMinCellZ;
    }
    // Current Time Vertical Gridline
    else if (elementKind == MSCollectionElementKindCurrentTimeVerticalGridline) {
        return (MSCollectionMinBackgroundZ + 1.0);
    }
    // Vertical Gridline
    else if (elementKind == MSCollectionElementKindVerticalGridline) {
        return MSCollectionMinBackgroundZ;
    }
    // Vertical Dash Gridline
    else if (elementKind == MSCollectionElementKindVerticalDashGridline) {
        return MSCollectionMinBackgroundZ;
    }
    return CGFLOAT_MIN;
}

#pragma mark Delegate Wrappers
//item开始时间
- (NSDateComponents *)startTimeForIndexPath:(NSIndexPath *)indexPath
{
    if ([self.cachedStartTimeDateComponents objectForKey:indexPath]) {
        return [self.cachedStartTimeDateComponents objectForKey:indexPath];
    }
    
    NSDate *date = [self.delegate collectionView:self.collectionView layout:self startTimeForItemAtIndexPath:indexPath];
    NSDateComponents *itemStartTimeDateComponents = [[NSCalendar currentCalendar] components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
    
    [self.cachedStartTimeDateComponents setObject:itemStartTimeDateComponents forKey:indexPath];
    return itemStartTimeDateComponents;
}
//item结束时间
- (NSDateComponents *)endTimeForIndexPath:(NSIndexPath *)indexPath
{
    if ([self.cachedEndTimeDateComponents objectForKey:indexPath]) {
        return [self.cachedEndTimeDateComponents objectForKey:indexPath];
    }
    
    NSDate *date = [self.delegate collectionView:self.collectionView layout:self endTimeForItemAtIndexPath:indexPath];
    NSDateComponents *itemEndTime = [[NSCalendar currentCalendar] components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
    
    [self.cachedEndTimeDateComponents setObject:itemEndTime forKey:indexPath];
    return itemEndTime;
}

//获取当前日期
- (NSDateComponents *)currentTimeDateComponents
{
    if ([self.cachedCurrentDateComponents objectForKey:@(0)]) {
        return [self.cachedCurrentDateComponents objectForKey:@(0)];
    }
    
    NSDate *date = [self.delegate currentTimeComponentsForCollectionView:self.collectionView layout:self];
    NSDateComponents *currentTime = [[NSCalendar currentCalendar] components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
    
    [self.cachedCurrentDateComponents setObject:currentTime forKey:@(0)];
    return currentTime;
}

#pragma mark - 根据当前坐标位置获取时间
-(NSDate *)timeAtPoint:(CGPoint)point{
    NSInteger numOfDayHeaders = 1 + self.collectionView.contentOffset.x / (self.hourWidth * 24 + self.contentMargin.left + self.contentMargin.right);
    CGFloat dayHeaderPadding = (self.dayColumnHeaderWidth + self.contentMargin.left) + (numOfDayHeaders - 1) * (self.dayColumnHeaderWidth + self.contentMargin.left + self.contentMargin.right);
    CGFloat pointOffsetX = point.x - dayHeaderPadding;
    NSTimeInterval timeInterval = nearbyintf((pointOffsetX / self.minuteWidth) * 60);
    NSDate *date = [self.delegate currentTimeComponentsForCollectionView:self.collectionView layout:self];
    NSDate *startDate = [[NSCalendar currentCalendar] startOfDayForDate:date];
    
    return [startDate dateByAddingTimeInterval:timeInterval];
}

//15分钟为最小单位
+(NSDate *)formatEventTime:(NSDate *)date {
    NSTimeInterval time = [date timeIntervalSince1970];
    time = nearbyint(time / (15 * 60)) * (15 * 60);
    return [NSDate dateWithTimeIntervalSince1970:time];
}

@end
